import json
import os
from copy import copy
from datetime import datetime

import yuan_shen.time_utils
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.schedulers.blocking import BlockingScheduler
from kivy.app import App
from kivy.core.text import LabelBase, DEFAULT_FONT
from kivy.core.window import Window
from kivy.graphics import Line, Color
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.checkbox import CheckBox
from kivy.uix.label import Label
from kivy.uix.popup import Popup

# 自定义组件
# from yuan_shen.yuan_shen import YuanShen
from yuan_shen_service import YuanShenService
from kivy_cust.ConfigComm import ConfigComm
from kivy_cust.MulitipleDropDown import MultiSelectDropDown
from kivy_cust.LabelComm import LabelComm
from kivy_cust.CellLayout import CellLayout
from kivy_cust.ColLayout import ColLayout
from kivy_cust.RowLayout import RowLayout
from kivy_cust.ButtonComm import ButtonComm
from kivy_cust.ButtonForm import ButtonForm
from kivy_cust.CheckBoxComm import CheckBoxComm
from kivy_cust.SpinnerComm import SpinnerComm
from kivy_cust.TextInputComm import TextInputComm


APP_TITLE = '抢个票吧V2'
Window.size = (1000, 700)  # 设置窗口默认大小
Window.minimum_width, Window.minimum_height = 800, 600  # 设置窗体最小值
Window.set_icon(ConfigComm.IMG_PIXEL_CAT)  # 设置程序图标
LabelBase.register(DEFAULT_FONT, ConfigComm.FONT_SIMKAI)


# 创建登录页面类
# 这个版本自定义标签都是写在.kv文件里
class FormLayout(BoxLayout):
    next_id = 0
    tickets = {}
    tickets_file = 'resources/data/tickets.json'
    cell_width = {'id': 50, 'tel': 120, 'pattern': 60, 'begin_time': 100, 'strategy': 60,
                  'strategy_vals': 0, 'ground_nos': 0, 'ground_times': 0, 'operate': 200}
    dictionary = {
        'valid_status': {
            '00': '生效',
            '01': '无效',
        },
        'run_status': {
            '00': '中止',
            '01': '运行',
        },
        'pattern': {
            '00': '抢票',
            '01': '监控',
        },
        'strategy': {
            '00': '日期',
            '01': '天',
            '02': '周',
        },
        'operate': {
            '00': '修改',
            '01': '删除',
            '02': '运行',
            '03': '中止',
        },
        'edit_type': {
            '00': '新增',
            '01': '修改',
            '02': '删除',
        },
        'run_type': {
            '00': '已终止',
            '01': '运行中',
            '02': '已暂停',
        },
    }
    # id  valid_status  tel  pattern  begin_time  strategy  strategy_vals  ground_nos  ground_times  operate  edit_type  run_type
    ticket_form = {}
    ticket_form_default = {
        'valid_status': '00',  # 有效
        'tel': '13214562789',
        'pattern': '00',
        'begin_time': '06:59:30',
        'strategy': '00',
        'ground_nos': '1',
        'ground_times': '09:00-10:00',
        'operate': '00,01,02,03',
        'edit_type': '00',  # 新增
        'run_type': '00',  # 已暂停
    }
    # scheduler = BlockingScheduler()  # 阻塞线程
    scheduler = BackgroundScheduler()  # 不阻塞线程
    scheduler.start()

    def __init__(self, **kwargs):
        super(FormLayout, self).__init__(**kwargs)

    def on_kv_post(self, base_widget):
        self.init_form()
        self.init_file()
        self.load_tickets_file()

    def edit(self, ticket, btn):
        ticket_form = copy(ticket)
        ticket_form.update(edit_type='01')
        self.set_form(form=ticket_form)

    def delete(self, ticket, btn):
        pop_val = self.tickets.pop(ticket.get('id'), False)
        if pop_val:
            if self.scheduler.get_job(ticket.get('id')):  # 有运行中的job则删除
                self.scheduler.remove_job(ticket.get('id'))
            self.refresh_table()
            self.dump_ticket_file()
        else:
            print('数据不存在！')

    def run(self, ticket, btn):
        # yuan_shen = YuanShen()
        expect_ground = [int(x) for x in ticket.get('ground_nos').split(',')]
        expect_time = [YuanShenService.time_dict.get(x) for x in ticket.get('ground_times').split(',')]
        monitor_date = [x for x in ticket.get('strategy_vals').split(',')]
        yuan_shen = YuanShenService(monitor_date=monitor_date, expect_ground=expect_ground, expect_time=expect_time, phone=ticket.get('phone'))
        # yuan_shen.test()
        if ticket.get('pattern') == '00':
            job = self.scheduler.add_job(yuan_shen.grab_tickets, trigger='date', id=str(ticket.get('id')))
        elif ticket.get('pattern') == '01':
            job = self.scheduler.add_job(yuan_shen.remaining_ticket_monitor, trigger='date', id=str(ticket.get('id')))
        else:
            print('未定义的模式！')
        if job:
            ticket.update(run_type='01')  # 运行中

    def abort(self, ticket, btn):
        self.scheduler.remove_job(str(ticket.get('id')))
        if not self.scheduler.get_job(ticket.get('id')):  # 任务是否还在
            ticket.update(run_type='00')  # 已终止

    def refresh(self):
        self.tickets = {}
        self.refresh_table()
        self.dump_ticket_file()

    def load_tickets_file(self, tickets_file=None):
        if not tickets_file:
            tickets_file = self.tickets_file
        next_id = 0
        tickets = {}
        with open(tickets_file, 'r', encoding='utf-8') as file:
            try:
                database = json.load(file)
                if isinstance(database, dict):
                    next_id = database.get('next_id', 0)
                    tickets = database.get('tickets', {})
                else:
                    raise ValueError('无效的数据！')
            except Exception as e:
                print(f'加载出错!\n{e}')
        self.next_id = next_id
        self.tickets = tickets
        self.refresh_table()

    def dump_ticket_file(self, tickets_file=None):
        if not tickets_file:
            tickets_file = self.tickets_file
        database = {
            'next_id': self.next_id,
            'tickets': self.tickets,
        }
        with open(tickets_file, 'w', encoding='utf-8') as file:
            json.dump(database, file, indent=2, ensure_ascii=False, )

    def setval(self):
        """测试方法"""
        # self.ids.grap.active = not self.ids.grap.active
        # self.ids.monitor.active = not self.ids.monitor.active
        # self.ids.grap.active = False
        self.ids.monitor.active = True

    def refresh_table(self):
        print('==> refresh_table')
        if self.tickets:
            if self.has_children(self.ids.ticket_blank):
                self.ids.ticket_blank.clear_widgets()
            self.ids.ticket_table.clear_widgets()
            for id, ticket in self.tickets.items():
                if not ticket.get('valid_status'):
                    continue

                row_layout = RowLayout()
                for field in ['id', 'tel', 'pattern', 'begin_time', 'strategy', 'strategy_vals', 'ground_nos', 'ground_times']:
                    cell_text = ticket.get(field)
                    if not cell_text:
                        cell_layout = CellLayout(text='')
                    else:
                        dict_val = self.get_dictionary_val(field, ticket.get(field))
                        cell_layout = CellLayout(text=dict_val if dict_val else cell_text)
                    if self.cell_width.get(field):
                        cell_layout.size_hint_x = None
                        cell_layout.width = self.cell_width.get(field)
                    row_layout.add_widget(cell_layout)

                operate = ticket.get('operate')
                if operate:
                    opt_layout = BoxLayout(padding=(5, 5), spacing=5)
                    for opt in operate.split(','):
                        opt_btn = ButtonComm(text=self.get_dictionary_val('operate', opt), font_size=17)
                        opt_btn.size = (40, 32)
                        # 绑定操作方法并传递按钮实例
                        def bind_button(opt, ticket, opt_btn):  # 不这样写，所有传入edit方法的ticket都是最后一行的值
                            if opt == '00':
                                opt_btn.bind(on_release=lambda instance: self.edit(ticket, opt_btn))
                            elif opt == '01':
                                opt_btn.bind(on_release=lambda instance: self.delete(ticket, opt_btn))
                            elif opt == '02':
                                opt_btn.bind(on_release=lambda instance: self.run(ticket, opt_btn))
                            elif opt == '03':
                                opt_btn.bind(on_release=lambda instance: self.abort(ticket, opt_btn))
                        bind_button(opt, ticket, opt_btn)
                        opt_layout.add_widget(opt_btn)
                    row_layout.add_widget(opt_layout)
                    if self.cell_width.get('operate'):
                        opt_layout.size_hint_x = None
                        opt_layout.width = self.cell_width.get('operate')
                self.ids.ticket_table.add_widget(row_layout)

            if self.ids.ticket_table not in self.ids.ticket_content.children:  # 表格是否显示
                self.ids.ticket_content.children.add_widget(self.self.ids.ticket_table)
        else:
            if self.has_children(self.ids.ticket_table):
                self.ids.ticket_table.clear_widgets()
            if not self.has_children(self.ids.ticket_blank):
                blank_label = LabelComm(text='无数据')
                blank_label.size_hint = (1, None)
                blank_label.height = 80
                blank_label.halign = 'center'
                self.ids.ticket_blank.add_widget(blank_label)

    def commit_form(self):
        print(f'==>> commit_form ticket_form:{self.ticket_form}')
        check_result = self.commit_before()
        if not check_result:
            return
        try:
            ticket = copy(self.ticket_form)
            self.tickets.update({ticket.get('id'): ticket})
            self.reset_form()
            self.dump_ticket_file()
            self.refresh_table()
        except Exception as e:
            print(f'保存失败：{e}')

    def commit_before(self) -> bool:
        print('==>> commit_before')
        if self.ticket_form.get('edit_type') == '00':  # 新增
            if not self.ticket_form.get('id'):
                self.ticket_form.update(id=str(self.next_id))
                self.next_id = self.next_id + 1
        self.ticket_form.update(valid_status='00')
        self.ticket_form.update(run_status='00')
        self.ticket_form.update(operate='00,01,02,03')
        if self.ids.tel.text:
            self.ticket_form['tel'] = self.ids.tel.text
        patterns = self.get_checkbox_vals(self.ids.pattern)
        self.ticket_form.update(pattern=patterns[0] if patterns else '')
        strategy = self.get_checkbox_vals(self.ids.strategy)
        self.ticket_form.update(strategy=strategy[0] if strategy else '')
        if self.ids.time_hour.text and self.ids.time_minute.text and self.ids.time_second.text:
            self.ticket_form['begin_time'] = self.ids.time_hour.text + ':' + self.ids.time_minute.text + ':' + self.ids.time_second.text
        self.ticket_form.update(strategy_vals=self.ids.strategy_vals.text)
        self.ticket_form.update(ground_nos=self.ids.ground_nos.text)
        self.ticket_form.update(ground_times=self.ids.ground_times.text)

        tips = ''
        if not self.ticket_form.get('tel'):
            tips += '手机号必输\n'
        if not self.ticket_form.get('pattern'):
            tips += '模式必输\n'
        if not self.ticket_form.get('begin_time'):
            tips += '执行时间必输\n'
        if not self.ticket_form.get('strategy'):
            tips += '策略必输\n'
        if not self.ticket_form.get('strategy_vals') or '请选择' in self.ticket_form.get('strategy_vals'):
            tips += '策略值必输\n'
        if not self.ticket_form.get('ground_nos') or '请选择' in self.ticket_form.get('ground_nos'):
            tips += '门票场地必输\n'
        if not self.ticket_form.get('ground_times') or '请选择' in self.ticket_form.get('ground_times'):
            tips += '门票时间必输\n'
        print(f'tips:{tips}')  # commit  tips
        if len(tips):
            tips = tips[:-1]
            popup = Popup(title='提示', content=Label(text=tips), size_hint=(None, None), size=(300, 200))
            popup.open()
            return False
        return True

    def reset_form(self):
        print('==> reset_form')
        self.set_form()

    def set_form(self, form=ticket_form_default):
        """
        不设置form参数时，取ticket_form_default，效果为重置表单；如：self.set_form()
        设置form参数时，使用设置的参数，效果为根据指定参数反显表单；如：self.set_form(form={'id': 1, 'tel': '123'})
        设置form参数为None时，走else逻辑，效果为清空表单；如self.set_form(form=None)
        :param form:
        :return:
        """
        print('==>> set_form')
        if form:
            self.ticket_form = form
            # id  valid_status  tel  pattern  begin_time  strategy  strategy_vals  ground_nos  ground_times  operate
            self.ids.tel.text = self.ticket_form.get('tel') or ''
            self.ids.grap.active = self.ticket_form.get('pattern') == '00'
            self.ids.monitor.active = self.ticket_form.get('pattern') == '01'
            self.ids.time_hour.text = self.ticket_form.get('begin_time')[:2] or '00'
            self.ids.time_minute.text = self.ticket_form.get('begin_time')[3:5] or '00'
            self.ids.time_second.text = self.ticket_form.get('begin_time')[6:] or '00'
            self.ids.strategy_00.active = self.ticket_form.get('strategy') == '00'
            self.ids.strategy_01.active = self.ticket_form.get('strategy') == '01'
            self.ids.strategy_02.active = self.ticket_form.get('strategy') == '02'
            self.ids.strategy_vals.text = self.ticket_form.get('strategy_vals') or '请选择策略值'
            self.ids.strategy_vals_dropdown.selected_options = self.ticket_form.get('strategy_vals').split(',') if self.ticket_form.get('strategy_vals') else ''
            self.ids.ground_nos.text = self.ticket_form.get('ground_nos') or '请选择门票场地'
            self.ids.ground_nos_dropdown.selected_options = self.ticket_form.get('ground_nos').split(',') if self.ticket_form.get('ground_nos') else ''
            self.ids.ground_times.text = self.ticket_form.get('ground_times') or '请选择门票时间'
            self.ids.ground_times_dropdown.selected_options = self.ticket_form.get('ground_times').split(',') if self.ticket_form.get('ground_times') else ''
        else:
            self.ticket_form = {}
            # id  valid_status  tel  pattern  begin_time  strategy  strategy_vals  ground_nos  ground_times  operate
            self.ids.tel.text = ''
            self.ids.grap.active = False
            self.ids.monitor.active = False
            self.ids.time_hour.text = ''
            self.ids.time_minute.text = ''
            self.ids.time_second.text = ''
            self.ids.strategy_00.active = False
            self.ids.strategy_01.active = False
            self.ids.strategy_02.active = False
            self.ids.strategy_vals.text = '请选择策略值'
            self.ids.ground_nos.text = '请选择门票场地'
            self.ids.ground_times.text = '请选择门票时间'

    def init_form(self):
        print('==> init_form')
        self.ticket_form.update(**self.ticket_form_default)
        self.set_form(form=self.ticket_form)

    def strategy_check(self, checkbox):
        strategy = checkbox.text
        print(f'strategy_check:{strategy} {checkbox.active}')
        if checkbox.active:
            # self.ticket_form.update(strategy=strategy)
            if strategy == '00':  # 日期
                strategy_vals_options = [(yuan_shen.time_utils.get_days_after(x, format_str='%Y-%m-%d')) for x in range(15)]
            elif strategy == '01':  # 天
                strategy_vals_options = [str(x) for x in range(8)]
            elif strategy == '02':  # 周
                strategy_vals_options = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
            else:
                print('未定义的策略！')
            self.ids.strategy_vals_dropdown.options = strategy_vals_options
            self.ids.strategy_vals_dropdown.selected_options = []
        else:
            pass

    def get_checkbox_vals(self, widget):
        vals = []
        for child in widget.children:
            if isinstance(child, CheckBox) and child.active:
                vals.append(child.text)
        return vals

    def has_children(self, node) -> bool:
        return bool(node.children)

    def init_file(self, file=None):
        if not file:
            file = self.tickets_file
        path = file[:file.rindex('/')]
        if not os.path.exists(path):
            os.makedirs(path)
        if not os.path.exists(file):
            open(file, 'w').close()

    def get_dictionary_val(self, dict_type, code):
        dict_type = self.dictionary.get(dict_type)
        val = None
        if dict_type:
            val = dict_type.get(code)
        return val


class TicketApp(App):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.ConfigComm = None

    def build(self):
        self.title = APP_TITLE
        self.ConfigComm = ConfigComm
        root = Builder.load_file('yuan_shen_tool.kv')
        return root


# 运行应用程序
if __name__ == '__main__':
    TicketApp().run()
